package quartz.controller;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scheduling.config.*;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.scheduling.support.ScheduledMethodRunnable;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("scheduled")
public class ScheduledController implements SchedulingConfigurer {
    ScheduledTaskRegistrar scheduled;
    @Autowired(required = false)
    ScheduledAnnotationBeanPostProcessor scheduledProcessor;
    final ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @GetMapping("queryjob")
    Object queryjob() {
        List<Object> list = new LinkedList<>();
        Field field = ReflectionUtils.findField(ScheduledTask.class, "future");
        field.setAccessible(true);
        for (ScheduledTask scheduledTask : scheduledProcessor.getScheduledTasks()) {
            Map<String, Object> map = new HashMap<>();
            BeanWrapper beanWrapper = new BeanWrapperImpl(scheduledTask.getTask());
            for (PropertyDescriptor propertyDescriptor : beanWrapper.getPropertyDescriptors()) {
                String name = propertyDescriptor.getName();
                map.put(name, beanWrapper.getPropertyValue(name));
            }
            ScheduledFuture<Task> future = (ScheduledFuture<Task>) ReflectionUtils.getField(field, scheduledTask);
            map.put("isDone", future.isDone());
            list.add(map);
        }
        return list;
    }

    @GetMapping("start")
    void start(String className, String name, Long interval, Long initialDelay, String expression) {
        for (ScheduledTask scheduledTask : scheduledProcessor.getScheduledTasks()) {
            Method method = ((ScheduledMethodRunnable) scheduledTask.getTask().getRunnable()).getMethod();
            if (!method.getName().equals(name) || !method.getDeclaringClass().getName().equals(className)) continue;
            scheduledTask.cancel();

            Task task = scheduledTask.getTask();
            if (task instanceof IntervalTask) {
                Field field = ReflectionUtils.findField(IntervalTask.class, "interval");
                field.setAccessible(true);
                ReflectionUtils.setField(field, task, interval);
                field = ReflectionUtils.findField(IntervalTask.class, "initialDelay");
                field.setAccessible(true);
                ReflectionUtils.setField(field, task, initialDelay);
            } else {
                Field field = ReflectionUtils.findField(TriggerTask.class, "trigger");
                field.setAccessible(true);
                ReflectionUtils.setField(field, task, new CronTrigger(expression));
                field = ReflectionUtils.findField(CronTask.class, "expression");
                if (field != null) {
                    field.setAccessible(true);
                    ReflectionUtils.setField(field, task, expression);
                }
            }

            ScheduledTask temp = null;
            if (task instanceof FixedDelayTask) {
                temp = scheduled.scheduleFixedDelayTask((FixedDelayTask) task);
            } else if (task instanceof FixedRateTask) {
                temp = scheduled.scheduleFixedRateTask((FixedRateTask) task);
            } else if (task instanceof CronTask) {
                temp = scheduled.scheduleCronTask((CronTask) task);
            } else if (task instanceof TriggerTask) {
                temp = scheduled.scheduleTriggerTask((TriggerTask) task);
            } else continue;

            Field field = ReflectionUtils.findField(ScheduledTask.class, "future");
            field.setAccessible(true);
            ReflectionUtils.setField(field, scheduledTask, ReflectionUtils.getField(field, temp));
            return;
        }
    }

    @GetMapping("stop")
    void stop(String className, String name) {
        for (ScheduledTask scheduledTask : scheduledProcessor.getScheduledTasks()) {
            Method method = ((ScheduledMethodRunnable) scheduledTask.getTask().getRunnable()).getMethod();
            if (!method.getDeclaringClass().getName().equals(className) || !method.getName().equals(name)) continue;
            scheduledTask.cancel();
            return;
        }
    }

    @GetMapping("one")
    void one(String className, String name) {
        for (ScheduledTask scheduledTask : scheduledProcessor.getScheduledTasks()) {
            Method method = ((ScheduledMethodRunnable) scheduledTask.getTask().getRunnable()).getMethod();
            if (!method.getDeclaringClass().getName().equals(className) || !method.getName().equals(name)) continue;
            threadPoolTaskExecutor.execute(scheduledTask.getTask().getRunnable());
            return;
        }
    }

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        scheduled = taskRegistrar;
    }
}